﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Windows;
using Analyzer;
using Analyzer.ViewModel;
using Model;

namespace ReportBrowser.ViewModel
{
    internal class MessagesViewModel : ViewModelBase, IDataErrorInfo
    {
        #region Lifecycle

        public MessagesViewModel(MainWindowViewModel parent)
        {
            this.Parent = parent;
            this.NumberOfMessagesToShow = 10;
            MessageViewModel.InstanceSelected += new EventHandler(MessageViewModel_InstanceSelected);
            this.PropertyChanged += new PropertyChangedEventHandler(MessagesViewModel_PropertyChanged);
            #region Desig-time data
            if (DesignerProperties.GetIsInDesignMode(new System.Windows.DependencyObject()) == true)
            {
                for (int i = 0; i < 3; i++)
                {
                    Message_FxCopMessage fcm =
                        new Message_FxCopMessage()
                        {
                            Created = DateTime.Now,
                            FixCategory = "fix kategória" + i.ToString(),
                            Status = "státusz" + i.ToString(),
                            FxCopIssues = new EntityCollection<FxCopIssue>()
                        };
                    Message m =
                        new Message()
                        {
                            AssemblyName = "asszembli" + i.ToString(),
                            ModuleName = "modul" + i.ToString(),
                            TypeName = "típus" + i.ToString(),
                            MemberName = "tag" + i.ToString(),
                            NamespaceName = "névtér" + i.ToString(),
                            Message_FxCopMessage = fcm
                        };
                    for (int j = 0; j < 2; j++)
                    {
                        fcm.FxCopIssues.Add(
                            new FxCopIssue()
                            {
                                Certainty = 99,
                                File = "fájl" + i.ToString() + j.ToString(),
                                Level = "szint" + i.ToString() + j.ToString(),
                                Line = "sor" + i.ToString() + j.ToString(),
                                Name = "név" + i.ToString() + j.ToString(),
                                Path = "út" + i.ToString() + j.ToString()
                            });
                    }
                    Messages.Add(new MessageViewModel(m, this.Context));
                }
            }
            #endregion Desig-time data
        }

        #endregion Lifecycle

        #region Properties

        private ActualEntities pContext;
        public ActualEntities Context
        {
            get { return pContext; }
            set
            {
                if (value != pContext)
                {
                    pContext = value;
                    OnPropertyChanged("Context");
                }
            }
        }

        private MainWindowViewModel Parent { get; set; }

        public List<Message> Model { get; set; }

        #region Presentation

        private ObservableCollection<MessageViewModel> pMessages;
        public ObservableCollection<MessageViewModel> Messages
        {
            get
            {
                if (pMessages == null)
                    pMessages = new ObservableCollection<MessageViewModel>();
                return pMessages;
            }
        }

        public int MessagesCount
        {
            get
            {
                if (this.Context != null)
                    return this.Context.Messages.Count();
                else
                    return 0;
            }
        }

        private int pNumberOfMessagesToShow;
        public int NumberOfMessagesToShow
        {
            get { return pNumberOfMessagesToShow; }
            set
            {
                if (value != pNumberOfMessagesToShow)
                {
                    pNumberOfMessagesToShow = value;
                    OnPropertyChanged("NumberOfMessagesToShow");
                }
            }
        }

        private bool pIsNumberOfMessagesToShowEnabled;
        public bool IsNumberOfMessagesToShowEnabled
        {
            get { return pIsNumberOfMessagesToShowEnabled; }
            set
            {
                if (value != pIsNumberOfMessagesToShowEnabled)
                {
                    pIsNumberOfMessagesToShowEnabled = value;
                    OnPropertyChanged("IsNumberOfMessagesToShowEnabled");
                }
            }
        }

        public MessageViewModel SelectedMessage { get; private set; }

        #endregion Presentation

        #endregion Properties

        #region Methods

        internal void Refresh(FilterType filterType, bool isProportional)
        {
            if (this.Context != null)
            {
                MessageViewModel.ClearInstances();
                DispatchService.Dispatch(() => this.Messages.Clear());
                switch (filterType)
                {
                    case FilterType.None:
                        break;
                    case FilterType.ShowAll:
                        SetAllMessages();
                        break;
                    case FilterType.QualityProfileFilter:
                        SetMessagesByQualityProfile(isProportional);
                        break;
                    case FilterType.CustomFilter:
                        SetMessagesByCustomProfile(isProportional);
                        break;
                    default:
                        break;
                }
            }
        }

        private void SetAllMessages()
        {
            this.Model = new List<Message>(this.Context.Messages);
            DispatchService.Dispatch(() =>
                {
                    foreach (var message in this.Model)
                        this.Messages.Add(new MessageViewModel(message, this.Context));
                });
            this.NumberOfMessagesToShow = this.Messages.Count;
        }

        private void SetMessagesByQualityProfile(bool isProportional)
        {
            #region Init
            if (this.Context.QualityProfileItems.Count() == 0
                && this.Parent.FilterType == FilterType.QualityProfileFilter)
            {
                MessageBox.Show("Nincs minőségi profil rögzítve! Az összes üzenet megjelenítésre kerül.",
                    "", MessageBoxButton.OK, MessageBoxImage.Information);
                this.Parent.FilterType = FilterType.ShowAll;
                //SetAllMessages();
                return;
            }
            int sumOfCategoryWeights = this.Context.QualityProfileItems.Sum(qpi => qpi.Weight);
            Dictionary<string, int> neededMessagePerCategory = new Dictionary<string, int>();
            Dictionary<string, int> currentMessagePerCategory = new Dictionary<string, int>();
            foreach (var item in this.Context.QualityProfileItems)
            {
                neededMessagePerCategory.Add(
                    item.CategoryName,
                    (int)((double)this.NumberOfMessagesToShow / (double)sumOfCategoryWeights * (double)item.Weight));
                currentMessagePerCategory.Add(item.CategoryName, 0);
            }
            this.Model = new List<Message>(this.Context.Messages);
            #endregion Init
            #region Sort messages
            this.Model.Sort(
                (m1, m2) =>
                {
                    //The first item is the most important, and the last is the least
                    double d = m2.GetWeight() - m1.GetWeight();
                    if (d < 0)
                        return -1;
                    else if (d == 0)
                        return 0;
                    else//if (d > 0)
                        return 1;
                });
            #endregion Sort messages
            #region Do the work
            for (int i = 0; i < this.NumberOfMessagesToShow && i < this.Model.Count; i++)
            {
                Message message = this.Model[i];
                bool added = false;
                foreach (var mapping in message.Rule.Mappings)
                {
                    if (currentMessagePerCategory[mapping.CategoryName]
                        < neededMessagePerCategory[mapping.CategoryName])
                    {
                        if (added == false)
                        {
                            DispatchService.Dispatch(() =>
                                this.Messages.Add(new MessageViewModel(message, this.Context)));
                            added = true;
                        }
                        currentMessagePerCategory[mapping.CategoryName] += 1;
                    }
                }
            }
            if (isProportional == false)
            {
                for (int i = 0; i < this.NumberOfMessagesToShow
                    && this.Messages.Count < this.NumberOfMessagesToShow; i++)
                {
                    Message message = this.Model[i];
                    if (this.Messages.Any(mvm => mvm.Model.Equals(message) == false))
                        DispatchService.Dispatch(() =>
                            this.Messages.Add(new MessageViewModel(message, this.Context)));
                }
            }//TODO isProportional==true
            #endregion Do the work
        }

        private void SetMessagesByCustomProfile(bool isProportional)
        {
            #region Init
            this.Model = new List<Message>(this.Context.Messages);
            #endregion Init
            #region Sort messages
            this.Model.Sort(
                (m1, m2) =>
                {
                    //The first item is the most important, and the last is the least
                    double d = GetCustomWeight(m2) - GetCustomWeight(m1);
                    if (d < 0)
                        return -1;
                    else if (d == 0)
                        return 0;
                    else//if (d > 0)
                        return 1;
                });
            #endregion Sort messages
            for (int i = 0; i < this.NumberOfMessagesToShow && i < this.Model.Count; i++)
            {
                if (this.Model.Count > 0)
                {
                    Message message = this.Model[i];
                    DispatchService.Dispatch(() =>
                        this.Messages.Add(new MessageViewModel(message, this.Context)));
                }
                else
                    break;
            }
            //TODO: use isProportional
        }

        private double GetCustomWeight(Message message)
        {
            double weight = 0;
            foreach (var mapping in message.Rule.Mappings)
            {
                if (this.Parent.FilterOptions.Any(qpi => qpi.Weight > 0
                    && qpi.CategoryName.Equals(mapping.CategoryName)))
                    weight += 1;
            }
            return weight;
        }

        #endregion Methods

        #region EventHandlers

        void MessageViewModel_InstanceSelected(object sender, EventArgs e)
        {
            MessageViewModel senderMvm = sender as MessageViewModel;
            if (senderMvm != null)
            {
                if (senderMvm.IsSelected == true)
                    this.SelectedMessage = senderMvm;
                else
                    this.SelectedMessage = null;
                OnPropertyChanged("SelectedMessage");
            }
        }

        void MessagesViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "NumberOfMessagesToShow":
                    Refresh(this.Parent.FilterType, this.Parent.IsProportional);
                    break;
                case "Context":
                    OnPropertyChanged("MessagesCount");
                    break;
                default:
                    break;
            }
        }

        #endregion EventHandlers

        #region IDataErrorInfo Members

        public string Error
        {
            get { throw new NotImplementedException(); }
        }

        public string this[string columnName]
        {
            get
            {
                string msg = null;
                switch (columnName)
                {
                    case "NumberOfMessagesToShow":
                        if (this.NumberOfMessagesToShow < 0)
                            msg = "A megjelenítendő üzenetek száma nagyobb kell legyen, mint 0 !";
                        break;
                    default:
                        throw new ArgumentException(
                            "Unrecognised property " + columnName + " !", "columnName");
                }
                return msg;
            }
        }

        #endregion IDataErrorInfo Members
    }
}
